/*
 * Copyright (C) 2018 Tom Li <tomli at tomli.me>.
 *
 * This software is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option) any
 * later version.
 *
 * This software is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * See COPYING for terms and conditions.
 *
 * GPL or LGPL does __not__ require attribution beyond preserving and
 * following the license, but if you find my code is useful or inspirational
 * to your own project, I'd be thankful if you attribute my name.
 */

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

#include "syscalls.h"

#define VARNAME  "TESTVAR"
#define VARSIZE  35970

void demo(void);


__at(0xA5F4) static uint16_t fillrect_color;
__at(0x4000) static uint8_t bank1[16384];


int main(void) {
    /* toggle 15 MHz CPU frequency */
    uint8_t orig_cpu = cpuclk_get();
    cpuclk_set(CPUCLK_FULL);

    /* cleanup the screen and reset the cursor */
    clear_screen();
    cursor_row = 0;
    cursor_col = 0;

    /*
     * First example: Hello, world.
     * 
     * On different machines, the methods for inputs and outputs are
     * different. SDCC's solution to this problem is defining a prototype
     * for "putchar()", etc, and requiring a user's to supply their own
     * implementation in order to make "printf()", etc, useful.
     * 
     * My implementation of "putchar()" can be found in "syscalls.c".
     *
     * NOTE: stdio library is HUGE. This program is less than 900 bytes,
     * but stdio makes it compile to 4000+ bytes! If size and speed matters,
     * don't use stdio library. But for most programs doing simple calculations,
     * it doesn't, feel free to use them.
     */
    printf("Hello, world.\n\n");

    printf("...(press a key)...\n");
    getkey();

    /* second demo */
    demo();

exit:
    /* wait for a keypress before exit */
    getkey();
    clear_screen();

    /* restore the original CPU frequency */
    cpuclk_set(orig_cpu);

    return 0;
}

void demo(void) {
    uint16_t page;
    uint16_t addr;

    /* looking for the Flash page and offset for our APPVAR */
    if (findsym(FINDSYM_APPVAR, VARNAME, &page, &addr) != 0) {
        printf("error:\n");
        printf("cannot find data variable.\n");
        return;
    }
    else {
        /* TODO: add a magic header for sanity check */
    }

    /* 
     * Save the original page number in Bank1, and map our page to
     * Bank1 instead.
     */
    uint16_t orig_page = bank1_get();
    bank1_swap(page);

    /* initial x, y position, and maximum x position */
    uint16_t x = 30;
    uint16_t y = 50;
    uint16_t x_max = 255 + x;

    uint16_t idx = addr;
    for (uint16_t i = 0; i < VARSIZE / 3; i++) {
        uint8_t length = bank1[idx];
        idx++;
        if (idx == 0x4000U) {
            page++;
            bank1_swap(page);
            idx = 0;
        }

        uint16_t color = bank1[idx];
        idx++;
        if (idx == 0x4000U) {
            page++;
            bank1_swap(page);
            idx = 0;
        }

        color |= ((bank1[idx]) << 8);
        idx++;
        if (idx == 0x4000U) {
            page++;
            bank1_swap(page);
            idx = 0;
        }

        /* 
         * This converts BGR to RGB by swapping the first/last five bits.
         * For the last five bits, we extract and bitshift it to the beginning,
         * and for the first five bits, we extract and bitstift it to the end.
         * Then, we clear the original first and last five bits, and apply the
         * bitshifted version on it to swap them.
         */
        uint16_t tmp = (color & 0x001FU) << 11;
        tmp |= (color & 0xF800U) >> 11;
        color &= 0xFFE0U & 0x07FFU;
        color |= tmp;

        fillrect_color = color;

        uint16_t x_end = x + length;

        if (x_end < x_max) {
            fillrect(x, x_end, y, y + 1);
            x = x_end;
        }
        else if (x_end == x_max) {
            fillrect(x, x_end, y, y + 1);
            x = 30;
            y++;
        }
        else {
            fillrect(x, x_max, y, y + 1);
            x_end -= 255;
            y++;
            fillrect(30, x_end, y, y + 1);
            x = x_end;
        }
    }

    bank1_swap(orig_page);
}
